home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / network / file-tra / rdist-6.1 / rdist-6 / rdist-6.1.0-linuxpl2 / src / child.c next >
Encoding:
C/C++ Source or Header  |  1995-06-05  |  13.4 KB  |  590 lines

  1. /*
  2.  * Copyright (c) 1983 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char RCSid[] = 
  36. "$Id: child.c,v 6.25 1994/03/31 02:56:25 mcooper Exp $";
  37.  
  38. static char sccsid[] = "@(#)docmd.c    5.1 (Berkeley) 6/6/85";
  39.  
  40. static char copyright[] =
  41. "@(#) Copyright (c) 1983 Regents of the University of California.\n\
  42.  All rights reserved.\n";
  43. #endif /* not lint */
  44.  
  45. /*
  46.  * Functions for rdist related to children
  47.  */
  48.  
  49. #include "defs.h"
  50. #include <sys/types.h>
  51. #include <sys/wait.h>
  52. #if    defined(NEED_SYS_SELECT_H)
  53. #include <sys/select.h>
  54. #endif    /* NEED_SYS_SELECT_H */
  55.  
  56. typedef enum _PROCSTATE {
  57.     PSrunning,
  58.     PSdead
  59. } PROCSTATE;
  60.  
  61. /*
  62.  * Structure for child rdist processes mainted by the parent
  63.  */
  64. struct _child {
  65.     char           *c_name;            /* Name of child */
  66.     int        c_readfd;        /* Read file descriptor */
  67.     pid_t        c_pid;            /* Process ID */
  68.     PROCSTATE       c_state;        /* Running? */
  69.     struct _child  *c_next;            /* Next entry */
  70. };
  71. typedef struct _child CHILD;
  72.  
  73. static CHILD           *childlist = NULL;    /* List of children */
  74. int             activechildren = 0;    /* Number of active children */
  75. extern int        maxchildren;        /* Max active children */
  76. static int         needscan = FALSE;    /* Need to scan children */
  77.  
  78. /*
  79.  * Remove a child that has died (exited) 
  80.  * from the list of active children
  81.  */
  82. static void removechild(child)
  83.     CHILD *child;
  84. {
  85.     register CHILD *pc, *prevpc;
  86.  
  87.     debugmsg(DM_CALL, "removechild(%s, %d, %d) start",
  88.          child->c_name, child->c_pid, child->c_readfd);
  89.  
  90.     /*
  91.      * Find the child in the list
  92.      */
  93.     for (pc = childlist, prevpc = NULL; pc != NULL; 
  94.          prevpc = pc, pc = pc->c_next)
  95.         if (pc == child) 
  96.             break;
  97.  
  98.     if (pc == NULL)
  99.         error("RemoveChild called with bad child %s %d %d",
  100.               child->c_name, child->c_pid, child->c_readfd);
  101.     else {
  102.         /*
  103.          * Remove the child
  104.          */
  105. #if    defined(POSIX_SIGNALS)
  106.         sigset_t set;
  107.  
  108.         sigemptyset(&set);
  109.         sigaddset(&set, SIGCHLD);
  110.         sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
  111. #else    /* !POSIX_SIGNALS */
  112.         int oldmask;
  113.  
  114.         oldmask = sigblock(sigmask(SIGCHLD));
  115. #endif    /* POSIX_SIGNALS */
  116.  
  117.         if (prevpc != NULL)
  118.             prevpc->c_next = pc->c_next;
  119.         else
  120.             childlist = pc->c_next;
  121.  
  122. #if    defined(POSIX_SIGNALS)
  123.         sigprocmask(SIG_UNBLOCK, &set, (sigset_t *)NULL);
  124. #else
  125.         sigsetmask(oldmask);
  126. #endif    /* POSIX_SIGNALS */
  127.  
  128.         (void) free(child->c_name);
  129.         --activechildren;
  130.         (void) close(child->c_readfd);
  131.         (void) free(pc);
  132.     }
  133.  
  134.     debugmsg(DM_CALL, "removechild() end");
  135. }
  136.  
  137. /*
  138.  * Create a totally new copy of a child.
  139.  */
  140. static CHILD *copychild(child)
  141.     CHILD *child;
  142. {
  143.     register CHILD *newc;
  144.  
  145.     newc = (CHILD *) xmalloc(sizeof(CHILD));
  146.  
  147.     newc->c_name = strdup(child->c_name);
  148.     newc->c_readfd = child->c_readfd;
  149.     newc->c_pid = child->c_pid;
  150.     newc->c_state = child->c_state;
  151.     newc->c_next = NULL;
  152.  
  153.     return(newc);
  154. }
  155.  
  156. /*
  157.  * Add a child to the list of children.
  158.  */            
  159. static void addchild(child)
  160.     CHILD *child;
  161. {
  162.     register CHILD *pc;
  163.  
  164.     debugmsg(DM_CALL, "addchild() start\n");
  165.  
  166.     pc = copychild(child);
  167.     pc->c_next = childlist;
  168.     childlist = pc;
  169.  
  170.     ++activechildren;
  171.  
  172.     debugmsg(DM_MISC,
  173.          "addchild() created '%s' pid %d fd %d (active=%d)\n",
  174.          child->c_name, child->c_pid, child->c_readfd, activechildren);
  175. }
  176.  
  177. /*
  178.  * Read input from a child process.
  179.  */
  180. static void readchild(child)
  181.     CHILD *child;
  182. {
  183.     char rbuf[BUFSIZ];
  184.     int amt;
  185.  
  186.     debugmsg(DM_CALL, "[readchild(%s, %d, %d) start]", 
  187.          child->c_name, child->c_pid, child->c_readfd);
  188.  
  189.     /*
  190.      * Check that this is a valid child.
  191.      */
  192.     if (child->c_name == NULL || child->c_readfd <= 0) {
  193.         debugmsg(DM_MISC, "[readchild(%s, %d, %d) bad child]",
  194.              child->c_name, child->c_pid, child->c_readfd);
  195.         return;
  196.     }
  197.  
  198.     /*
  199.      * Read from child and display the result.
  200.      */
  201.     while ((amt = read(child->c_readfd, rbuf, sizeof(rbuf))) > 0) {
  202.         /* XXX remove these debug calls */
  203.         debugmsg(DM_MISC, "[readchild(%s, %d, %d) got %d bytes]", 
  204.              child->c_name, child->c_pid, child->c_readfd, amt);
  205.  
  206.         {
  207.             int wamt=amt, ramt, woff=0;
  208.             while ((ramt = write(fileno(stdout), rbuf+woff, wamt)) > 0) {
  209.                 woff += ramt; wamt -= ramt;
  210.                 debugmsg(DM_MISC,
  211.                      "[readchild(%s, %d, %d) only wrote %d/%d bytes]",
  212.                      child->c_name, child->c_pid, child->c_readfd, 
  213.                      ramt, wamt);
  214.             }
  215.         }
  216.         debugmsg(DM_MISC, "[readchild(%s, %d, %d) write done]",
  217.              child->c_name, child->c_pid, child->c_readfd);
  218.     }
  219.  
  220.     debugmsg(DM_MISC, "readchild(%s, %d, %d) done: amt = %d errno = %d\n",
  221.          child->c_name, child->c_pid, child->c_readfd, amt, errno);
  222.  
  223.     /* 
  224.      * See if we've reached EOF 
  225.      */
  226.     if (amt == 0)
  227.         debugmsg(DM_MISC, "readchild(%s, %d, %d) at EOF\n",
  228.              child->c_name, child->c_pid, child->c_readfd);
  229. }
  230.  
  231. /*
  232.  * Wait for processes to exit.  If "block" is true, then we block
  233.  * until a process exits.  Otherwise, we return right away.  If
  234.  * a process does exit, then the pointer "statval" is set to the
  235.  * exit status of the exiting process, if statval is not NULL.
  236.  */
  237. static int waitproc(statval, block)
  238.     int *statval;
  239.     int block;
  240. {
  241.     WAIT_ARG_TYPE status;
  242.     int pid, exitval;
  243.  
  244.     debugmsg(DM_CALL, "waitproc() %s, active children = %d...\n", 
  245.          (block) ? "blocking" : "nonblocking", activechildren);
  246.  
  247. #if    WAIT_TYPE == WAIT_WAITPID
  248.     pid = waitpid(-1, &status, (block) ? 0 : WNOHANG);
  249. #else
  250. #if    WAIT_TYPE == WAIT_WAIT3
  251.     pid = wait3(&status, (block) ? 0 : WNOHANG, NULL);
  252. #endif    /* WAIT_WAIT3 */
  253. #endif    /* WAIT_WAITPID */
  254.  
  255. #if    defined(WEXITSTATUS)
  256.     exitval = WEXITSTATUS(status);
  257. #else
  258.     exitval = status.w_retcode;
  259. #endif    /* defined(WEXITSTATUS) */
  260.  
  261.     if (pid > 0 && exitval != 0) {
  262.         nerrs++;
  263.         debugmsg(DM_MISC, 
  264.              "Child process %d exited with status %d.\n",
  265.              pid, exitval);
  266.     }
  267.  
  268.     if (statval)
  269.         *statval = exitval;
  270.  
  271.     debugmsg(DM_CALL, "waitproc() done (activechildren = %d)\n", 
  272.          activechildren);
  273.  
  274.     return(pid);
  275. }
  276.  
  277. /*
  278.  * Check to see if any children have exited, and if so, read any unread
  279.  * input and then remove the child from the list of children.
  280.  */
  281. static void reap()
  282. {
  283.     register CHILD *pc;
  284.     int status = 0;
  285.     pid_t pid;
  286.  
  287.     debugmsg(DM_CALL, "reap() called\n");
  288.  
  289.     /*
  290.      * Reap every child that has exited.  Break out of the
  291.      * loop as soon as we run out of children that have
  292.      * exited so far.
  293.      */
  294.     for ( ; ; ) {
  295.         /*
  296.          * Do a non-blocking check for exiting processes
  297.          */
  298.         pid = waitproc(&status, FALSE);
  299.         debugmsg(DM_MISC, 
  300.              "reap() pid = %d status = %d activechildren=%d\n",
  301.              pid, status, activechildren);
  302.  
  303.         /*
  304.          * See if a child really exited
  305.          */
  306.         if (pid == 0)
  307.             break;
  308.         if (pid < 0) {
  309.             if (errno != ECHILD)
  310.                 error("Wait failed: %s", SYSERR);
  311.             break;
  312.         }
  313.  
  314.         /*
  315.          * Find the process (pid) and mark it as dead.
  316.          */
  317.         for (pc = childlist; pc; pc = pc->c_next)
  318.             if (pc->c_pid == pid) {
  319.                 needscan = TRUE;
  320.                 pc->c_state = PSdead;
  321.             }
  322.  
  323.     }
  324.  
  325.     /*
  326.      * Reset signals
  327.      */
  328.     (void) signal(SIGCHLD, reap);
  329.  
  330.     debugmsg(DM_CALL, "reap() done\n");
  331. }
  332.  
  333. /*
  334.  * Scan the children list to find the child that just exited, 
  335.  * read any unread input, then remove it from the list of active children.
  336.  */
  337. static void childscan() 
  338. {
  339.     register CHILD *pc, *nextpc;
  340.     
  341.     debugmsg(DM_CALL, "childscan() start");
  342.  
  343.     for (pc = childlist; pc; pc = nextpc) {
  344.         nextpc = pc->c_next;
  345.         if (pc->c_state == PSdead) {
  346.             readchild(pc);
  347.             removechild(pc);
  348.         }
  349.     }
  350.  
  351.     needscan = FALSE;
  352.     debugmsg(DM_CALL, "childscan() end");
  353. }
  354.  
  355. /*
  356. #if    defined HAVE_SELECT
  357.  *
  358.  * Wait for children to send output for us to read.
  359.  *
  360. #else    !HAVE_SELECT
  361.  *
  362.  * Wait up for children to exit.
  363.  *
  364. #endif
  365.  */
  366. extern void waitup()
  367. {
  368. #if    defined(HAVE_SELECT)
  369.     register int count;
  370.     register CHILD *pc;
  371.     fd_set rchildfds;
  372.  
  373.     debugmsg(DM_CALL, "waitup() start\n");
  374.  
  375.     if (needscan)
  376.         childscan();
  377.  
  378.     if (activechildren <= 0)
  379.         return;
  380.  
  381.     /*
  382.      * Settup which children we want to select() on.
  383.      */
  384.     FD_ZERO(&rchildfds);
  385.     for (pc = childlist; pc; pc = pc->c_next)
  386.         if (pc->c_readfd > 0) {
  387.             debugmsg(DM_MISC, "waitup() select on %d (%s)\n",
  388.                  pc->c_readfd, pc->c_name);
  389.             FD_SET(pc->c_readfd, &rchildfds);
  390.         }
  391.  
  392.     /*
  393.      * Actually call select()
  394.      */
  395.     /* XXX remove debugmsg() calls */
  396.     debugmsg(DM_MISC, "waitup() Call select(), activechildren=%d\n", 
  397.          activechildren);
  398.  
  399.     count = select(FD_SETSIZE, &rchildfds, (fd_set *) NULL, 
  400.                (fd_set *) NULL, (struct timeval *) NULL);
  401.  
  402.     debugmsg(DM_MISC, "waitup() select returned %d activechildren = %d\n", 
  403.          count, activechildren);
  404.  
  405.     /*
  406.      * select() will return count < 0 and errno == EINTR when
  407.      * there are no active children left.
  408.      */
  409.     if (count < 0) {
  410.         if (errno != EINTR)
  411.             error("Select failed reading children input: %s", 
  412.                   SYSERR);
  413.         return;
  414.     }
  415.  
  416.     /*
  417.      * This should never happen.
  418.      */
  419.     if (count == 0) {
  420.         error("Select returned an unexpected count of 0.");
  421.         return;
  422.     }
  423.  
  424.     /*
  425.      * Go through the list of children and read from each child
  426.      * which select() detected as ready for reading.
  427.      */
  428.     for (pc = childlist; pc && count > 0; pc = pc->c_next) {
  429.         /* 
  430.          * Make sure child still exists 
  431.          */
  432.         if (pc->c_name && kill(pc->c_pid, 0) < 0 && 
  433.             errno == ESRCH) {
  434.             debugmsg(DM_MISC, 
  435.                  "waitup() proc %d (%s) died unexpectedly!",
  436.                  pc->c_pid, pc->c_name);
  437.             pc->c_state = PSdead;
  438.             needscan = TRUE;
  439.         }
  440.  
  441.         if (pc->c_name == NULL ||
  442.             !FD_ISSET(pc->c_readfd, &rchildfds))
  443.             continue;
  444.  
  445.         readchild(pc);
  446.         --count;
  447.     }
  448.  
  449. #else    /* !defined(HAVE_SELECT) */
  450.  
  451.     /*
  452.      * The non-select() version of waitproc()
  453.      */
  454.     debugmsg(DM_CALL, "waitup() start\n");
  455.  
  456.     if (waitproc((int *) NULL, TRUE) > 0)
  457.         --activechildren;
  458.  
  459. #endif    /* defined(HAVE_SELECT) */
  460.     debugmsg(DM_CALL, "waitup() end\n");
  461. }
  462.  
  463. /*
  464.  * Spawn (create) a new child process for "cmd".
  465.  */
  466. extern int spawn(cmd, cmdlist)
  467.     struct cmd *cmd;
  468.     struct cmd *cmdlist;
  469. {
  470.     pid_t pid;
  471.     int fildes[2];
  472.     char *childname = cmd->c_name;
  473.  
  474.     if (pipe(fildes) < 0) {
  475.         error("Cannot create pipe for %s: %s", childname, SYSERR);
  476.         return(-1);
  477.     }
  478.  
  479.     pid = fork();
  480.     if (pid == (pid_t)-1) {
  481.         error("Cannot spawn child for %s: fork failed: %s", 
  482.               childname, SYSERR);
  483.         return(-1);
  484.     } else if (pid > 0) {
  485.         /*
  486.          * Parent
  487.          */
  488.         static CHILD newchild;
  489.  
  490. #if    defined(FORK_MISSES)
  491.         /*
  492.          * XXX Some OS's have a bug where fork does not
  493.          * always return properly to the parent
  494.          * when a number of forks are done very quicky.
  495.          */
  496.         sleep(2);
  497. #endif    /* FORK_MISSES */
  498.  
  499.         /* Receive notification when the child exits */
  500.         (void) signal(SIGCHLD, reap);
  501.  
  502.         /* Settup the new child */
  503.         newchild.c_next = NULL;
  504.         newchild.c_name = childname;
  505.         newchild.c_readfd = fildes[PIPE_READ];
  506.         newchild.c_pid = pid;
  507.         newchild.c_state = PSrunning;
  508.  
  509.         /* We're not going to write to the child */
  510.         (void) close(fildes[PIPE_WRITE]);
  511.  
  512.         /* Set non-blocking I/O */
  513.         if (setnonblocking(newchild.c_readfd, TRUE) < 0) {
  514.             error("Set nonblocking I/O failed: %s", SYSERR);
  515.             return(-1);
  516.         }
  517.  
  518.         /* Add new child to child list */
  519.         addchild(&newchild);
  520.  
  521.         /* Mark all other entries for this host as assigned */
  522.         markassigned(cmd, cmdlist);
  523.  
  524.         debugmsg(DM_CALL,
  525.              "spawn() Forked child %d for host %s active = %d\n",
  526.              pid, childname, activechildren);
  527.         return(pid);
  528.     } else {
  529.         /* 
  530.          * Child 
  531.          */
  532.  
  533.         /* We're not going to read from our parent */
  534.         (void) close(fildes[PIPE_READ]);
  535.  
  536.         /* Make stdout and stderr go to PIPE_WRITE (our parent) */
  537.         if (dup2(fildes[PIPE_WRITE], (int)fileno(stdout)) < 0) {
  538.             error("Cannot duplicate stdout file descriptor: %s", 
  539.                   SYSERR);
  540.             return(-1);
  541.         }
  542.         if (dup2(fildes[PIPE_WRITE], (int)fileno(stderr)) < 0) {
  543.             error("Cannot duplicate stderr file descriptor: %s", 
  544.                   SYSERR);
  545.             return(-1);
  546.         }
  547.  
  548.         return(0);
  549.     }
  550. }
  551.  
  552.  
  553. /*
  554.  * Enable or disable non-blocking I/O mode.
  555.  *
  556.  * Code is from INN by Rich Salz.
  557.  */
  558. #if    NBIO_TYPE == NBIO_IOCTL
  559. #include <sys/ioctl.h>
  560.  
  561. int setnonblocking(fd, flag)
  562.     int fd;
  563.     int flag;
  564. {
  565.     int state;
  566.  
  567.     state = flag ? 1 : 0;
  568.     return(ioctl(fd, FIONBIO, (char *)&state));
  569. }
  570.  
  571. #endif    /* NBIO_IOCTL */
  572.  
  573.  
  574. #if    NBIO_TYPE == NBIO_FCNTL
  575. int setnonblocking(fd, flag)
  576.     int fd;
  577.     int flag;
  578. {
  579.     int    mode;
  580.  
  581.     if ((mode = fcntl(fd, F_GETFL, 0)) < 0)
  582.         return(-1);
  583.     if (flag)
  584.         mode |= FNDELAY;
  585.     else
  586.         mode &= ~FNDELAY;
  587.     return(fcntl(fd, F_SETFL, mode));
  588. }
  589. #endif    /* NBIO_FCNTL */
  590.